package com.maven.qqspider.ui.action;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;

import com.maven.qqspider.common.Constant;
import com.maven.qqspider.util.ExcelEngine;
import com.maven.qqspider.util.log4j.Log;

public class SearchAction {

	private static Logger logger = Log.getLogger();
	private static SearchAction instance = null;

	static final String URLHOST = "http://soren.qzone.qq.com/cgi-bin/soren/cgi_userinfo_search";

	static final String PREFIX_QQNUMBER = "<p class=\"result_user\">";
	static final String PREFIX_SEX = "<p><strong>性别：</strong><span>";
	static final String PREFIX_BIRTHDAY = "<p><strong>生日：</strong><span>";
	static final String PREFIX_PROFESSIONAL = "<p><strong>职业：</strong><span>";
	static final String PREFIX_AGE = "<p><strong>年龄：</strong><span>";
	static final String PREFIX_LOCATION = "<p><strong>居住地：</strong><span>";
	static final String PREFIX_COMPANYNAME = "<p><strong>公司名称：</strong><span>";
	static final String PREFIX_MARRIAGE = "<p><strong>婚姻：</strong><span>";
	static final String PREFIX_HOME = "<p><strong>故乡：</strong><span>";
	static final String PREFIX_COMPANYADDRESS = "<p><strong>公司地址：</strong><span>";

	static final String TOTALRESULT_MARK_PREFIX = "<div class=\"search_result\" id=\"userlist\" style=\"display:none;\">";
	static final String TOTALRESULT_MARK = "</strong>条结果";
	
	static final String REGEX_OTHERINFOR = ">([^Strong]|[^span]+?)<";
	static Pattern p_otherInfor = Pattern.compile(REGEX_OTHERINFOR);

	public String[] search(Map<String, String> pmap)
			throws MalformedURLException, UnsupportedEncodingException,
			IOException {
		boolean flag = true;
		String msg = "";

		// Get total result count
		int totalResultCount = getTotalResultCount(pmap, 0);
		if (totalResultCount == 0) {
			msg = "查询到  0  条结果";
			logger.info(msg);
			flag = false;
		}

		if (flag) {

			if (totalResultCount < 65530) {
				List<List<String>> dataList = new ArrayList<List<String>>();

				// Each page have 10 results, get total page count
				int totalPageCount = 0;
				if (totalResultCount % 10 > 0) {
					totalPageCount = totalResultCount / 10 + 1;
				} else {
					totalPageCount = totalResultCount / 10;
				}

				logger.info("totalPageCount = " + totalPageCount);

				// getOnePageResult返回一页的信息
				// List<String>是一行信息
				// List<List<String>>是一页的信息
				// List<List<String>> dataList是所有页的信息
				for (int i = 0; i < totalPageCount; i++) {
					List<List<String>> temp = getOnePageResult(pmap, i);
					for (int j = 0; j < temp.size(); j++) {
						dataList.add((List<String>)temp.get(j));
					}
				}

				/* Write QQ number into Excel file */
				write2Excel(dataList);

				logger.info("=======================================");
				logger.info("Write to Excel file completed");
				logger.info("=======================================");
			} else {
				flag = false;
				msg = "查询结果超过6万条，请增加查询条件以得到更详细的结果。";
				logger.error(msg);
			}
		}

		return new String[] { flag ? "true" : "false", msg };
	}

	public static SearchAction getInstence() {
		if (instance == null) {
			instance = new SearchAction();
		}
		return instance;
	}

	private static int getTotalResultCount(Map<String, String> pmap, int pageNo)
			throws MalformedURLException, IOException,
			UnsupportedEncodingException {

		setPageNo2ParamMap(pmap, pageNo);
		String params = getCombineParams(pmap);

		URL url = new URL(URLHOST + Constant.getQuestionMark() + params);
		logger.info("url = " + url);

		URLConnection rulConnection = url.openConnection();
		HttpURLConnection httpUrlConnection = (HttpURLConnection) rulConnection;

		httpUrlConnection.connect();
		logger.info("send ok");

		int code = httpUrlConnection.getResponseCode();
		logger.info("code: " + code);
		logger.info("ResponseMessage: "
				+ httpUrlConnection.getResponseMessage());

		// 读取响应的内容
		String sCurrentLine = "";
		int totalResult = 0;
		boolean totalResult_flag = false;
		if (code == 200) {
			InputStream is = httpUrlConnection.getInputStream();
			BufferedReader reader = new BufferedReader(new InputStreamReader(
					is, "gbk"));
			while ((sCurrentLine = reader.readLine()) != null)
				if (sCurrentLine != null) {
					
					if (sCurrentLine.indexOf(TOTALRESULT_MARK_PREFIX) != -1) {
						// logger.info(sCurrentLine);
						totalResult_flag = true;
					}

					if (totalResult_flag
							&& sCurrentLine.indexOf(TOTALRESULT_MARK) != -1) {
						totalResult = getTotalResultNumber(sCurrentLine);
					}
				}
		}

		// 断开连接
		httpUrlConnection.disconnect();
		logger.info("Connection disconnect");

		return totalResult;
	}

	private static void setPageNo2ParamMap(Map<String, String> pmap, int pageNo) {
		pmap.put("begin", String.valueOf(pageNo));
	}

	private static List<List<String>> getOnePageResult(
			Map<String, String> pmap, int pageNo) throws MalformedURLException,
			IOException, UnsupportedEncodingException {

		setPageNo2ParamMap(pmap, pageNo);
		logger.info("*** page no = " + pageNo + " ***");
		String params = getCombineParams(pmap);

		URL url = new URL(URLHOST + Constant.getQuestionMark() + params);
		logger.info("url = " + url);

		/**
		 * <code> 
		 * 	此处的urlConnection对象实际上是根据URL的请求协议(此处是http)
		 * 	生成的URLConnection类的子类HttpURLConnection,
		 * 	故此处最好将其转化为HttpURLConnection类型的对象,
		 *  以便用到HttpURLConnection更多的API.如下:
		 *  </code>
		 */
		URLConnection rulConnection = url.openConnection();
		HttpURLConnection httpUrlConnection = (HttpURLConnection) rulConnection;

		// 进行连接，但是实际上get request要在下一句的connection.getInputStream()
		// 函数中才会真正发到服务器
		httpUrlConnection.connect();
		logger.info("Connect");

		int code = httpUrlConnection.getResponseCode();
		logger.info("code: " + code);

		// 读取响应的内容
		String sCurrentLine = "";
		String sTotaLString = "";
		List<List<String>> pageList = new ArrayList<List<String>>();
		
		if (code == 200) {
			InputStream is = httpUrlConnection.getInputStream();
			BufferedReader reader = new BufferedReader(new InputStreamReader(
					is, "gbk"));

			// CORE function
			boolean flag = false;
			boolean flag_page = false;
			List<String> rowList = new ArrayList<String>();
			while ((sCurrentLine = reader.readLine()) != null) {
				
				if (sCurrentLine != null) {
					// System.out.println(sCurrentLine);
					sTotaLString = sTotaLString + sCurrentLine.trim();
					Matcher m_otherInfor = p_otherInfor.matcher(sCurrentLine);

					if (sCurrentLine.indexOf(PREFIX_QQNUMBER) != -1) {
						// System.out.println(sCurrentLine);
						String q = fetchQNumber(sCurrentLine);
						String email = q + "@QQ.com";
						rowList.add(email);
						flag = true;
					}
					
					flag_page = fetchOtherInformation(sCurrentLine, rowList, m_otherInfor);
					if(flag_page){
						List<String> list = new ArrayList<String>();
						for (int i = 0; i < rowList.size(); i++) {
							list.add(rowList.get(i));
						}
						pageList.add(list);
						
						rowList.removeAll(rowList);
						
						flag = false;
					}
				}
			}
			// logger.info("response:" + sTotaLString);
		}

		// 断开连接
		httpUrlConnection.disconnect();
		logger.info("Disconnect");

		return pageList;
	}

	private static boolean fetchOtherInformation(String sCurrentLine,
			List<String> rowList, Matcher m_otherInfor) {
		boolean flag = false;
		if (sCurrentLine.indexOf(PREFIX_SEX) != -1) {
			String sex = fetchSex(m_otherInfor);
			rowList.add(sex);
		}

		if (sCurrentLine.indexOf(PREFIX_BIRTHDAY) != -1) {
			String birthday = fetchBirthday(m_otherInfor);
			rowList.add(birthday);
		}

		if (sCurrentLine.indexOf(PREFIX_PROFESSIONAL) != -1) {
			logger.info(sCurrentLine);
			String professional = fetchProfessional(m_otherInfor);
			rowList.add(professional);
		}

		if (sCurrentLine.indexOf(PREFIX_AGE) != -1) {
			String age = fetchAge(m_otherInfor);
			rowList.add(age);
		}

		if (sCurrentLine.indexOf(PREFIX_LOCATION) != -1) {
			String location = fetchLocation(m_otherInfor);
			rowList.add(location);
		}

		if (sCurrentLine.indexOf(PREFIX_COMPANYNAME) != -1) {
			String campanyName = fetchCompanyName(m_otherInfor);
			rowList.add(campanyName);
		}

		if (sCurrentLine.indexOf(PREFIX_MARRIAGE) != -1) {
			String marriage = fetchMarriage(m_otherInfor);
			rowList.add(marriage);
		}

		if (sCurrentLine.indexOf(PREFIX_HOME) != -1) {
			String hometown = fetchHometown(m_otherInfor);
			rowList.add(hometown);
		}

		if (sCurrentLine.indexOf(PREFIX_COMPANYADDRESS) != -1) {
			String campanyAddress = fetchCompanyAddress(m_otherInfor);
			rowList.add(campanyAddress);
			flag = true;
		}
		
		return flag;
	}

	private static String fetchQNumber(String sCurrentLine) {
		/**
		 * sCurrentLine like: <code>
		 * 		<p class="result_user"><a class="nick" onclick="statistic.start(4);return true;" href="http://user.qzone.qq.com/313881577/profile" target="_blank"></a><a href="http://user.qzone.qq.com/313881577" onclick="statistic.start(5);return true;" target="_blank" class="get_qzone" title="访问空间"><span>访问空间</span></a><a href="http://vip.qzone.qq.com" target="_blank" class="vip_icon none" title="黄钻贵族"><span>黄钻贵族</span></a></p>
		 * </code>
		 */
		String temp = sCurrentLine.substring(132);

		/**
		 * temp.substring(0, 15) will be: <code>
		 * 		346315630/profi
		 * </code>
		 */
		temp = temp.substring(0, 15);
		String tempArr[] = temp.split("/");
		String q = tempArr[0];

		// System.out.println(temp);
		// System.out.println("q = " + q);
		return q;
	}

	private static String fetchSex(Matcher matcher) {
		/**
		 * Sex information will in line like below: <code>
		 * 			<p><strong>性别：</strong><span>女</span></p>
		 * </code>
		 */
		String sex = "";
		while(matcher.find()){
			String s = matcher.group();
			if(s.indexOf("性别") == -1){
				sex = removeSymbol(s);
			}
		}

		return sex;
	}

	private static String fetchBirthday(Matcher matcher) {
		/**
		 * Birthday information will in line like below: <code>
		 * 			<p><strong>生日：</strong><span>03月27日(白羊座)</span></p>
		 * </code>
		 */
		String birthday = "";
		while(matcher.find()){
			String s = matcher.group();
			if(s.indexOf("生日") == -1){
				birthday = removeSymbol(s);
			}
		}

		return birthday;
	}

	private static String fetchProfessional(Matcher matcher) {
		/**
		 * Professional information will in line like below: <code>
		 * 			<p><strong>职业：</strong><span><span style="background-color:yellow;color:red">护士</span></span></p>
		 * </code>
		 */
		StringBuffer professional = new StringBuffer();
		while(matcher.find()){
			String s = matcher.group();
			 
			if(s.indexOf("职业") == -1){
				s = removeSymbol(s);
				professional.append(s);
			}
		}

		return professional.toString();
	}

	private static String fetchAge(Matcher matcher) {
		/**
		 * Age information will in line like below: <code>
		 * 			<p><strong>年龄：</strong><span>23</span></p>
		 * </code>
		 */
		String age = "";
		while(matcher.find()){
			String s = matcher.group();
			if(s.indexOf("年龄") == -1){
				age = removeSymbol(s);
			}
		}

		return age;
	}

	private static String fetchLocation(Matcher matcher) {
		/**
		 * Location information will in line like below: <code>
		 * 			<p><strong>居住地：</strong><span>北京 平谷</span></p>
		 * </code>
		 */
		String location = "";
		while(matcher.find()){
			String s = matcher.group();
			if(s.indexOf("居住地") == -1){
				location = removeSymbol(s);
			}
		}

		return location;
	}

	private static String fetchCompanyName(Matcher matcher) {
		/**
		 * Company Name information will in line like below: <code>
		 * 			<p><strong>公司名称：</strong><span>未填写</span></p>
		 * </code>
		 */
		String campanyName = "";
		while(matcher.find()){
			String s = matcher.group();
			if(s.indexOf("公司名称") == -1){
				campanyName = removeSymbol(s);
			}
		}

		return campanyName;
	}

	private static String fetchMarriage(Matcher matcher) {
		/**
		 * Marriage information will in line like below: <code>
		 * 			<p><strong>婚姻：</strong><span>未婚</span></p>
		 * </code>
		 */
		String marriage = "";
		while(matcher.find()){
			String s = matcher.group();
			if(s.indexOf("婚姻") == -1){
				marriage = removeSymbol(s);
			}
		}

		return marriage;
	}

	private static String fetchHometown(Matcher matcher) {
		/**
		 * Home information will in line like below: <code>
		 * 			<p><strong>故乡：</strong><span>北京 平谷</span></p>
		 * </code>
		 */
		String home = "";
		while(matcher.find()){
			String s = matcher.group();
			if(s.indexOf("故乡") == -1){
				home = removeSymbol(s);
			}
		}

		return home;
	}

	private static String fetchCompanyAddress(Matcher matcher) {
		/**
		 * Company Address information will in line like below: <code>
		 * 			<p><strong>公司地址：</strong><span>北京 平谷</span></p>
		 * </code>
		 */
		String companyAddress = "";
		while(matcher.find()){
			String s = matcher.group();
			if(s.indexOf("公司地址") == -1){
				companyAddress = removeSymbol(s);
			}
		}

		return companyAddress;
	}

	private static int getTotalResultNumber(String sCurrentLine) {

		int totalResult;
		/**
		 * Result:<code>
		 * 		<span>(<strong>23</strong>条结果)</span>
		 * </code>
		 */
		// logger.info(sCurrentLine);

		String temp = sCurrentLine.substring(20);
		String[] tempArr = temp.split("<");
		temp = tempArr[0];
		if (temp.indexOf(",") != -1) {
			String[] arr = temp.split(",");
			StringBuffer sb = new StringBuffer();
			for (int i = 0; i < arr.length; i++) {
				sb.append(arr[i]);
			}
			totalResult = Integer.parseInt(sb.toString());
		} else {
			totalResult = Integer.parseInt(temp);
		}

		logger.info("TotalResult = " + totalResult);

		return totalResult;
	}

	private static void write2Excel(List<List<String>> dataList)
			throws IOException {
		String absolutePath = "D:" + File.separator;
		String fileName = "QSpider.xls";
		String fullPath = absolutePath + fileName;

		List<String> colNameList = new ArrayList<String>();
		colNameList.add("QQ邮件地址");
		colNameList.add("性别");
		colNameList.add("生日");
		colNameList.add("职业");
		colNameList.add("年龄");
		colNameList.add("居住地");
		colNameList.add("公司名称");
		colNameList.add("婚姻");
		colNameList.add("故乡");
		colNameList.add("公司地址");

		List<String> colTypeList = new ArrayList<String>();
		colTypeList.add("String");
		colTypeList.add("String");
		colTypeList.add("String");
		colTypeList.add("String");
		colTypeList.add("String");
		colTypeList.add("String");
		colTypeList.add("String");
		colTypeList.add("String");
		colTypeList.add("String");
		colTypeList.add("String");

		ExcelEngine.writeAExcel(fullPath, colNameList, colTypeList, dataList);
	}

	public static String getCombineParams(Map<String, String> pmap)
			throws UnsupportedEncodingException {
		StringBuffer params = new StringBuffer();

		// 性别
		// params.append("sex=" + pmap.get("sex") == null ? "-1" :
		// pmap.get("sex"));

		params.append("sex=");
		params.append(pmap.get("sex") == null ? "-1" : pmap.get("sex"));
		params.append("&");
		// 年龄 1
		params.append("a1=");
		params.append(pmap.get("a1") == null ? "-1" : pmap.get("a1"));
		params.append("&");
		// 年龄 2
		params.append("a2=");
		params.append(pmap.get("a2") == null ? "-1" : pmap.get("a2"));
		params.append("&");
		// 居住地 省
		params.append("pv=");
		params.append(pmap.get("pv") == null ? "0" : pmap.get("pv"));
		params.append("&");
		// 居住地 市
		params.append("ct=");
		params.append(pmap.get("ct") == null ? "0" : pmap.get("ct"));
		params.append("&");
		// 故乡 省
		params.append("hp=");
		params.append(pmap.get("hp") == null ? "0" : pmap.get("hp"));
		params.append("&");
		// 故乡 市
		params.append("hc=");
		params.append(pmap.get("hc") == null ? "0" : pmap.get("hc"));
		params.append("&");
		// 星座
		params.append("at=");
		params.append(pmap.get("at") == null ? "-1" : pmap.get("at"));
		params.append("&");
		// 生日 月
		params.append("month=");
		params.append(pmap.get("month") == null ? "-1" : pmap.get("month"));
		params.append("&");
		// 生日 日
		params.append("day=");
		params.append(pmap.get("day") == null ? "-1" : pmap.get("day"));
		params.append("&");
		// 婚姻状况
		params.append("mr=");
		params.append(pmap.get("mr") == null ? "-1" : pmap.get("mr"));
		params.append("&");
		// 血型
		params.append("bt=");
		params.append(pmap.get("bt") == null ? "-1" : pmap.get("bt"));
		params.append("&");
		// 职业
		params.append("cr=");
		params
				.append(encodeParam(pmap.get("cr") == null ? "" : pmap
						.get("cr")));
		params.append("&");
		// 公司名称
		params.append("cn=");
		params
				.append(encodeParam(pmap.get("cn") == null ? "" : pmap
						.get("cn")));
		params.append("&");
		// 公司地址省
		params.append("cap=");
		params.append(pmap.get("cap") == null ? "0" : pmap.get("cap"));
		params.append("&");
		// 公司地址市
		params.append("cac=");
		params.append(pmap.get("cac") == null ? "0" : pmap.get("cac"));
		params.append("&");
		// 喜爱的偶像
		params.append("idol=");
		params.append(encodeParam(pmap.get("idol") == null ? "" : pmap
				.get("idol")));
		params.append("&");
		// 喜爱的游戏
		params.append("game=");
		params.append(encodeParam(pmap.get("game") == null ? "" : pmap
				.get("game")));
		params.append("&");
		// 喜爱的数码产品
		params.append("it=");
		params
				.append(encodeParam(pmap.get("it") == null ? "" : pmap
						.get("it")));
		params.append("&");
		// 喜爱的运动
		params.append("sport=");
		params.append(encodeParam(pmap.get("sport") == null ? "" : pmap
				.get("sport")));
		params.append("&");
		// 喜爱的书籍
		params.append("book=");
		params.append(encodeParam(pmap.get("book") == null ? "" : pmap
				.get("book")));
		params.append("&");
		// 喜爱的电影电视剧
		params.append("tv=");
		params
				.append(encodeParam(pmap.get("tv") == null ? "" : pmap
						.get("tv")));
		params.append("&");
		// 喜爱的品牌
		params.append("cloth=");
		params.append(encodeParam(pmap.get("cloth") == null ? "" : pmap
				.get("cloth")));
		params.append("&");
		// 喜爱的音乐乐器
		params.append("cosm=");
		params.append(encodeParam(pmap.get("cosm") == null ? "" : pmap
				.get("cosm")));
		params.append("&");
		// 去过想去的地方
		params.append("place=");
		params.append(encodeParam(pmap.get("place") == null ? "" : pmap
				.get("place")));
		params.append("&");
		// 更多兴趣爱好
		params.append("interest=");
		params.append(encodeParam(pmap.get("interest") == null ? "" : pmap
				.get("interest")));
		params.append("&");
		//
		params.append("begin=");
		params.append(pmap.get("begin") == null ? "0" : pmap.get("begin"));
		params.append("&");
		//
		params.append("bSimple=");
		params.append(pmap.get("bSimple") == null ? "0" : pmap.get("bSimple"));

		// logger.info("params = " + params.toString());

		return params.toString();
	}

	private static String encodeParam(String param)
			throws UnsupportedEncodingException {
		return URLEncoder.encode(param, "GBK");
	}
	
	/**
	 * String like ">abc<"
	 * @return abc
	 */
	private static String removeAngleBrackets(String s){
		String result = "";
		if(s.indexOf("<") != -1){
			s = s.replace("<", "");
			result = s;
		}
		if(s.indexOf(">") != -1){
			s = s.replace(">", "");
			result = s;
		}
		
		return result;
	}
	
	private static String removeColon(String s){
		String result = "";
		if(s.indexOf(":") != -1){
			s = s.replace(":", "");
			result = s;
		} else if(s.indexOf("：") != -1){
			s = s.replace("：", "");
			result = s;
		} else {
			result = s;
		}
		return result;
	}
	
	private static String removeSymbol(String s) {
		s = removeAngleBrackets(s);
		s = removeColon(s);
		return s.trim(); 
	}

}
